#include "Geometry.h"
#include "sutil.h"
#include "device_util.h"
#include <string>
#include <optixu/optixu_math_namespace.h>

using namespace optix;
using namespace std;


GeometryInstance Scene::createParallelogram( const float3& anchor,
                          const float3& offset1,
                          const float3& offset2,
                          const float3& color)
{
  Geometry parallelogram = _ctx->createGeometry();
  parallelogram->setPrimitiveCount( 1u );
  parallelogram->setIntersectionProgram( _pgram_intersect );
  parallelogram->setBoundingBoxProgram( _pgram_bounding_box );

  float3 normal = normalize( cross( offset1, offset2 ) );
  float d       = dot( normal, anchor );
  float4 plane  = make_float4( normal, d );

  float3 v1 = offset1 / dot( offset1, offset1 );
  float3 v2 = offset2 / dot( offset2, offset2 );

  parallelogram["plane"]->setFloat( plane );
  parallelogram["anchor"]->setFloat( anchor );
  parallelogram["v1"]->setFloat( v1 );
  parallelogram["v2"]->setFloat( v2 );

  GeometryInstance gi = _ctx->createGeometryInstance( parallelogram,
      &_material,
      &_material+1 );
  gi["Kd"]->setFloat( color );
  gi["Ks"]->setFloat( 0.0f, 0.0f, 0.0f );
  gi["use_grid"]->setUint( 0u );
  gi["grid_color"]->setFloat( make_float3( 0.0f ) );
  gi["emissive_color"]->setFloat( 0.0f, 0.0f, 0.0f );
  return gi;
}

GeometryInstance Scene::createSphere( const float4& params, const float3& color) {
	Geometry sphere = _ctx->createGeometry();
	sphere->setPrimitiveCount( 1u );
	sphere->setIntersectionProgram( _sphere_intersect );
	sphere->setBoundingBoxProgram( _sphere_bounding_box );
	sphere["sphere"]->setFloat(params);

	GeometryInstance gi = _ctx->createGeometryInstance( sphere, &_material, &_material+1 );
	gi["Kd"]->setFloat( 0.0f, 0.0f, 0.0f );
  gi["Ks"]->setFloat( 1.0f, 1.0f, 1.0f );
  gi["use_grid"]->setUint( 0u );
  gi["grid_color"]->setFloat( make_float3( 0.0f ) );
  gi["emissive_color"]->setFloat( 0.0f, 0.0f, 0.0f );

	return gi;
}

string ptxpath( const std::string& target, const std::string& base )
{
  static std::string path;
  path = std::string(sutilSamplesPtxDir()) + "\\" + target + "_generated_" + base + ".ptx";
  return path.c_str();
}

Scene::Scene(Context ctx):_ctx(ctx) {

	 // Set up material
  _material = _ctx->createMaterial();
  _material->setClosestHitProgram( 0, _ctx->createProgramFromPTXFile( ptxpath( "sample1", "plain.cu"),
                                   "closest_hit") );
 
	_material->setClosestHitProgram( 1, _ctx->createProgramFromPTXFile( ptxpath( "sample1", "photon.cu"),
                                   "closest_hit") ); 

	_material->setAnyHitProgram( 3, _ctx->createProgramFromPTXFile( ptxpath( "sample1", "gather.cu"),
                                   "any_hit") ); 
  

	// Set up geometry programs
	std::string ptx_path = ptxpath( "sample1", "parallelogram.cu" );
  _pgram_bounding_box = _ctx->createProgramFromPTXFile( ptx_path, "bounds" );
  _pgram_intersect = _ctx->createProgramFromPTXFile( ptx_path, "intersect" );
	
  ptx_path = ptxpath( "sample1", "sphere.cu" );
	_sphere_bounding_box = _ctx->createProgramFromPTXFile( ptx_path, "bounds" );
  _sphere_intersect = _ctx->createProgramFromPTXFile( ptx_path, "intersect" );
	

	// Populate cornell box

	// create geometry instances
  std::vector<GeometryInstance> gis;

  const float3 white = make_float3( 0.8f, 0.8f, 0.8f );
  const float3 green = make_float3( 0.05f, 0.8f, 0.05f );
  const float3 red   = make_float3( 0.8f, 0.05f, 0.05f );
  const float3 black = make_float3( 0.0f, 0.0f, 0.0f );
  const float3 light = make_float3( 15.0f, 15.0f, 5.0f );

  // Floor
  gis.push_back( createParallelogram( make_float3( 0.0f, 0.0f, 0.0f ),
                                      make_float3( 0.0f, 0.0f, 559.2f ),
                                      make_float3( 556.0f, 0.0f, 0.0f ),
                                      white ) );

  // Ceiling
  gis.push_back( createParallelogram( make_float3( 0.0f, 548.8f, 0.0f ),
                                      make_float3( 556.0f, 0.0f, 0.0f ),
                                      make_float3( 0.0f, 0.0f, 559.2f ),
                                      white ) );

  // Back wall
  gis.push_back( createParallelogram( make_float3( 0.0f, 0.0f, 559.2f),
                                      make_float3( 0.0f, 548.8f, 0.0f),
                                      make_float3( 556.0f, 0.0f, 0.0f),
                                      white ) ); 

  // Right wall
  gis.push_back( createParallelogram( make_float3( 0.0f, 0.0f, 0.0f ),
                                      make_float3( 0.0f, 548.8f, 0.0f ),
                                      make_float3( 0.0f, 0.0f, 559.2f ),
                                      green ) );

  // Left wall
  gis.push_back( createParallelogram( make_float3( 556.0f, 0.0f, 0.0f ),
                                      make_float3( 0.0f, 0.0f, 559.2f ),
                                      make_float3( 0.0f, 548.8f, 0.0f ),
                                      red ) );

  // Short block
  gis.push_back( createParallelogram( make_float3( 130.0f, 165.0f, 65.0f),
                                      make_float3( -48.0f, 0.0f, 160.0f),
                                      make_float3( 160.0f, 0.0f, 49.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 290.0f, 0.0f, 114.0f),
                                      make_float3( 0.0f, 165.0f, 0.0f),
                                      make_float3( -50.0f, 0.0f, 158.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 130.0f, 0.0f, 65.0f),
                                      make_float3( 0.0f, 165.0f, 0.0f),
                                      make_float3( 160.0f, 0.0f, 49.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 82.0f, 0.0f, 225.0f),
                                      make_float3( 0.0f, 165.0f, 0.0f),
                                      make_float3( 48.0f, 0.0f, -160.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 240.0f, 0.0f, 272.0f),
                                      make_float3( 0.0f, 165.0f, 0.0f),
                                      make_float3( -158.0f, 0.0f, -47.0f),
                                      white ) );
	 

	// Tall block
  gis.push_back( createParallelogram( make_float3( 423.0f, 330.0f, 247.0f),
                                      make_float3( -158.0f, 0.0f, 49.0f),
                                      make_float3( 49.0f, 0.0f, 159.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 423.0f, 0.0f, 247.0f),
                                      make_float3( 0.0f, 330.0f, 0.0f),
                                      make_float3( 49.0f, 0.0f, 159.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 472.0f, 0.0f, 406.0f),
                                      make_float3( 0.0f, 330.0f, 0.0f),
                                      make_float3( -158.0f, 0.0f, 50.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 314.0f, 0.0f, 456.0f),
                                      make_float3( 0.0f, 330.0f, 0.0f),
                                      make_float3( -49.0f, 0.0f, -160.0f),
                                      white ) );
  gis.push_back( createParallelogram( make_float3( 265.0f, 0.0f, 296.0f),
                                      make_float3( 0.0f, 330.0f, 0.0f),
                                      make_float3( 158.0f, 0.0f, -49.0f),
                                      white ) );

	// Sphere
	// gis.push_back( createSphere( make_float4( 423.0f, 50.0f, 247.0f, 50.0f ), red ) );

  // Light
  gis.push_back( createParallelogram( make_float3( 343.0f, 548.7f, 227.0f),
                                      make_float3( 0.0f, 0.0f, 105.0f),
                                      make_float3( -130.0f, 0.0f, 0.0f),
                                      black) );

  gis.back()["emissive_color"]->setFloat( light );
	Light _light; 
	_light.anchor = make_float3( 343.0f, 548.6f, 227.0f);
  _light.v1     = make_float3( 0.0f, 0.0f, 105.0f);
  _light.v2     = make_float3( -130.0f, 0.0f, 0.0f);
  _light.direction = normalize(cross( _light.v1, _light.v2 ) ); 
  _light.power  = make_float3( 0.5e6f, 0.4e6f, 0.2e6f );
  _ctx["light"]->setUserData( sizeof(Light), &_light );


  // Create geometry group
  GeometryGroup geometry_group = _ctx->createGeometryGroup();
  geometry_group->setChildCount( static_cast<unsigned int>( gis.size() ) );
  for ( unsigned int i = 0; i < gis.size(); ++i )
    geometry_group->setChild( i, gis[i] );
  geometry_group->setAcceleration( _ctx->createAcceleration("Bvh","Bvh") );

  _ctx["top_object"]->set( geometry_group );
  _ctx["top_shadower"]->set( geometry_group );
}
